/*
 * Licensed to the Tai Ping Jin Ke
 *
 * Copyright (c) 2022 .
 * All rights reserved.
 * 项目名称：太平金科-基础组件封装层-ElasticSearch-缺省实现
 * 版权说明：本软件属太平金融科技服务(上海)有限公司所有，在未获得太平金融科技服务(上海)有限公司正式授权情况下，任何企业和个人，不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。
 */
package com.taipingframework.boot.es.conditions;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.json.JSONArray;
import com.taipingframework.boot.es.conditions.interfaces.Compare;
import com.taipingframework.boot.es.conditions.interfaces.Func;
import com.taipingframework.boot.es.conditions.interfaces.ISqlSegment;
import com.taipingframework.boot.es.enums.SqlKeyword;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 查询条件封装
 *
 * @param <T>
 * @param <R>
 * @param <Children>
 */
public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T, R, Children>>
        extends Wrapper<T>
        implements Compare<Children, R>, Func<Children, R> {

    /**
     * 占位符
     */
    protected final Children typedThis = (Children) this;

    /**
     * 格式化SQL
     */
    protected final QueryBuilder createExpression(ISqlSegment fieldName, SqlKeyword sqlKeyword, Object val) {
        QueryBuilder condition;
        switch (sqlKeyword) {
            // 1）精确匹配 - 等于
            case EQ:
                condition = QueryBuilders.termQuery(fieldName.getSqlSegment(), val);
                break;

            // 2）精确匹配 - 不等于
            case NE:
                condition = QueryBuilders.boolQuery().mustNot(QueryBuilders.termQuery(fieldName.getSqlSegment(), val));
                break;

            // 3）范围匹配 - 在范围内
            case IN:
                List<Object> list = new JSONArray(val).parallelStream().collect(Collectors.toList());
                condition = QueryBuilders.termsQuery(fieldName.getSqlSegment(), list);
                break;

            // 4）匹配范围 - 不在范围内
            case NOT_IN:
                condition = QueryBuilders.boolQuery().mustNot(QueryBuilders.termsQuery(fieldName.getSqlSegment(), val));
                break;

            // 5）区间匹配
            case GT: // >
                condition = QueryBuilders.rangeQuery(fieldName.getSqlSegment()).gt(castTimestamp(val));
                break;
            case GE: // >=
                condition = QueryBuilders.rangeQuery(fieldName.getSqlSegment()).gte(castTimestamp(val));
                break;
            case LT: // <
                condition = QueryBuilders.rangeQuery(fieldName.getSqlSegment()).lt(castTimestamp(val));
                break;
            case LE: // <=
                condition = QueryBuilders.rangeQuery(fieldName.getSqlSegment()).lte(castTimestamp(val));
                break;

            // 6）模糊匹配
            case LIKE:
                condition = QueryBuilders.wildcardQuery(fieldName.getSqlSegment(), String.format("*%s*", val));
                break;
            case LIKE_LEFT:
                condition = QueryBuilders.wildcardQuery(fieldName.getSqlSegment(), String.format("*%s", val));
                break;
            case LIKE_RIGHT:
                condition = QueryBuilders.wildcardQuery(fieldName.getSqlSegment(), String.format("%s*", val));
                break;

            // 7）模糊匹配 - 取反
            case NOT_LIKE:
                condition = QueryBuilders.boolQuery().mustNot(QueryBuilders.wildcardQuery(fieldName.getSqlSegment(), String.format("*%s*", val)));
                break;

            // 8）非空匹配
            case IS_NOT_NULL:
                condition = QueryBuilders.existsQuery(fieldName.getSqlSegment());
                break;

            // 9）空匹配
            case IS_NULL:
                condition = QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery(fieldName.getSqlSegment()));
                break;

            // 10）默认
            default:
                condition = null;
                break;
        }

        return condition;
    }

    /**
     * 日期类型转时间戳
     */
    private Object castTimestamp(Object obj) {
        long value;
        if (obj instanceof Date) {
            value = ((Date) obj).getTime();
        } else if (obj instanceof LocalDateTime) {
            value = LocalDateTimeUtil.toEpochMilli((LocalDateTime) obj);
        } else if (obj instanceof LocalDate) {
            value = LocalDateTimeUtil.toEpochMilli((LocalDate) obj);
        } else {
            return obj;
        }
        return value;
    }

    /**
     * 对sql片段进行组装
     *
     * @param expression 条件表达式
     * @return children
     */
    protected Children doIt(QueryBuilder expression) {
        if (expression != null) {
            boolQueryBuilder.must(expression);
        }
        return typedThis;
    }

    /**
     * 普通查询条件
     *
     * @param column     属性
     * @param sqlKeyword SQL 关键词
     * @param val        条件值
     */
    protected Children addCondition(R column, SqlKeyword sqlKeyword, Object val) {
        return doIt(createExpression(() -> columnToString(column), sqlKeyword, val));
    }

    /**
     * 必要的初始化
     */
    protected void initNeed() {
        aggregationBuilders = new ArrayList<>();
        sortBuilders = new ArrayList<>();
        searchQueryBuilder = new NativeSearchQueryBuilder();
        boolQueryBuilder = new BoolQueryBuilder();
    }

    /**
     * 清空数据
     */
    @Override
    protected void clear() {
        aggregationBuilders.clear();
        sortBuilders.clear();
        searchQueryBuilder = null;
        boolQueryBuilder.must().clear();
        boolQueryBuilder = null;
        entityClass = null;
    }

    /**
     * 获取 columnName
     */
    protected abstract String columnToString(R column);

    /**
     * 获取 fieldName
     */
    protected abstract String columnToField(R column);

    //region implements Compare<Children, R>

    /**
     * 等于 =
     *
     * @param column 字段
     * @param val    值
     * @return children
     */
    @Override
    public Children eq(R column, Object val) {
        return addCondition(column, SqlKeyword.EQ, val);
    }

    /**
     * 不等于 &lt;&gt;
     *
     * @param column 字段
     * @param val    值
     * @return children
     */
    @Override
    public Children ne(R column, Object val) {
        return addCondition(column, SqlKeyword.NE, val);
    }

    /**
     * 大于 &gt;
     *
     * @param column 字段
     * @param val    值
     * @return children
     */
    @Override
    public Children gt(R column, Object val) {
        return addCondition(column, SqlKeyword.GT, val);
    }

    /**
     * 大于等于 &gt;=
     *
     * @param column 字段
     * @param val    值
     * @return children
     */
    @Override
    public Children ge(R column, Object val) {
        return addCondition(column, SqlKeyword.GE, val);
    }

    /**
     * 小于 &lt;
     *
     * @param column 字段
     * @param val    值
     * @return children
     */
    @Override
    public Children lt(R column, Object val) {
        return addCondition(column, SqlKeyword.LT, val);
    }

    /**
     * 小于等于 &lt;=
     *
     * @param column 字段
     * @param val    值
     * @return children
     */
    @Override
    public Children le(R column, Object val) {
        return addCondition(column, SqlKeyword.LE, val);
    }

    /**
     * LIKE '%值%'
     *
     * @param column 字段
     * @param val    值
     * @return children
     */
    @Override
    public Children like(R column, Object val) {
        return addCondition(column, SqlKeyword.LIKE, val);
    }

    /**
     * NOT LIKE '%值%'
     *
     * @param column 字段
     * @param val    值
     * @return children
     */
    @Override
    public Children notLike(R column, Object val) {
        return addCondition(column, SqlKeyword.NOT_LIKE, val);
    }

    /**
     * LIKE '%值'
     *
     * @param column 字段
     * @param val    值
     * @return children
     */
    @Override
    public Children likeLeft(R column, Object val) {
        return addCondition(column, SqlKeyword.LIKE_LEFT, val);
    }

    /**
     * LIKE '值%'
     *
     * @param column 字段
     * @param val    值
     * @return children
     */
    @Override
    public Children likeRight(R column, Object val) {
        return addCondition(column, SqlKeyword.LIKE_RIGHT, val);
    }

    //endregion

    //region implements Func<Children, R>

    /**
     * 字段 IS NULL
     * <p>例: isNull("name")</p>
     *
     * @param column 字段
     * @return children
     */
    @Override
    public Children isNull(R column) {
        return addCondition(column, SqlKeyword.IS_NULL, null);
    }

    /**
     * 字段 IS NOT NULL
     * <p>例: isNotNull("name")</p>
     *
     * @param column 字段
     * @return children
     */
    @Override
    public Children isNotNull(R column) {
        return addCondition(column, SqlKeyword.IS_NOT_NULL, null);
    }

    /**
     * 字段 IN (value.get(0), value.get(1), ...)
     * <p>例: in("id", Arrays.asList(1, 2, 3, 4, 5))</p>
     *
     * <li> 如果集合为 empty 则不会进行 sql 拼接 </li>
     *
     * @param column 字段
     * @param coll   数据集合
     * @return children
     */
    @Override
    public Children in(R column, Collection<?> coll) {
        return addCondition(column, SqlKeyword.IN, coll);
    }

    /**
     * 字段 NOT IN (value.get(0), value.get(1), ...)
     * <p>例: notIn("id", Arrays.asList(1, 2, 3, 4, 5))</p>
     *
     * @param column 字段
     * @param coll   数据集合
     * @return children
     */
    @Override
    public Children notIn(R column, Collection<?> coll) {
        return addCondition(column, SqlKeyword.NOT_IN, coll);
    }

    //endregion

}
